package com.ljf.authority.domain.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ljf.authority.domain.repository.*;
import com.ljf.authority.dto.command.AuthorityBaseApiTabulationActivationCommand;
import com.ljf.authority.dto.command.AuthorityBaseApiTabulationCommand;
import com.ljf.authority.dto.command.AuthorityBaseApiTabulationMenuCommand;
import com.ljf.authority.dto.command.AuthorityBaseApiTabulationTemplateCommand;
import com.ljf.authority.dto.query.*;
import com.ljf.authority.enums.AuthorityBaseApiTabulationMethodEnum;
import com.ljf.authority.enums.AuthorityBaseApiTabulationPermissionTypeEnum;
import com.ljf.authority.enums.AuthorityBaseApiTabulationSynchronizationTypeEnum;
import com.ljf.authority.enums.AuthorityBaseApiTabulationTemplateEnum;
import com.ljf.authority.model.AuthorityBaseApiTabulation;
import com.ljf.authority.model.AuthorityRelButtonApi;
import com.ljf.authority.utils.CodeGenerateUtil;
import com.ljf.authority.vo.*;
import com.ljf.common.domain.entity.KeyCountModel;
import com.ljf.common.domain.service.BasisDomainService;
import com.ljf.common.enums.ActivationEnum;
import com.ljf.common.exception.ServiceException;
import com.ljf.common.utils.CollectionUtil;
import com.ljf.common.utils.EnumUtil;
import com.ljf.common.utils.MathUtil;
import com.ljf.common.utils.StringUtil;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * API列表定义  领域服务
 *
 * @author LJF
 * @since 2024-10-25
 */
@Component
@AllArgsConstructor
public class AuthorityBaseApiTabulationDomainService extends BasisDomainService<AuthorityBaseApiTabulationRepository, AuthorityBaseApiTabulationQuery, AuthorityBaseApiTabulationVO, AuthorityBaseApiTabulation, AuthorityBaseApiTabulationCommand> {

    private final AuthorityRelButtonApiRepository buttonApiRepository;
    private final AuthorityRelPermissionGroupMenuRepository permissionGroupMenuRepository;
    private final AuthorityRelPermissionGroupUserRepository permissionGroupUserRepository;
    private final AuthorityBaseMenuTabulationRepository menuTabulationRepository;

    /**
     * 校验接口权限
     *
     * @param user
     * @param url
     * @param method
     * @return
     * @throws ServiceException
     */
    public boolean checkAuthorityApi(String user, String url, Integer method) throws ServiceException {
        if (StringUtil.isEmpty(url)) {
            throw new ServiceException("权限异常:请求地址不能为空");
        }
        if (url.contains("/")) {
            String endUrl = url.substring(url.lastIndexOf('/') + 1);
            if (StringUtil.isNotEmpty(endUrl) && MathUtil.isNumber(endUrl)) {
                url = url.substring(0, url.lastIndexOf('/') + 1) + "{id}";
            }
        }
        AuthorityBaseApiTabulationVO apiTabulation = super.queryOne(
                AuthorityBaseApiTabulationQuery.builder().url(url).method(method).activation(ActivationEnum.ACTIVATED.getCode()).build(),
                "权限异常:非法请求",
                "权限异常:配置异常"
        );
        if (StringUtil.isEmpty(apiTabulation.getCode())) {
            throw new ServiceException("权限异常:API编码异常");
        }
        if (Objects.equals(apiTabulation.getPermissionType(), AuthorityBaseApiTabulationPermissionTypeEnum.NOT_AUTHENTICATION.getCode())) {
            return Objects.equals(apiTabulation.getSynchronizationType(), AuthorityBaseApiTabulationSynchronizationTypeEnum.SYNCHRONIZATION.getCode());
        }
        if (!Objects.equals(apiTabulation.getPermissionType(), AuthorityBaseApiTabulationPermissionTypeEnum.AUTHENTICATION.getCode())) {
            throw new ServiceException("权限异常:API权限类型异常");
        }
        List<AuthorityRelButtonApiVO> relButtonApiList = buttonApiRepository.selectList(
                AuthorityRelButtonApiQuery.builder().apiCode(apiTabulation.getCode()).build()
        );
        if (CollectionUtil.isEmpty(relButtonApiList)) {
            throw new ServiceException("权限异常:API无关联按钮");
        }
        List<String> buttonCodeList = relButtonApiList.stream()
                .map(AuthorityRelButtonApiVO::getButtonCode).distinct().collect(Collectors.toList());
        List<AuthorityRelPermissionGroupMenuVO> permissionGroupMenuList = permissionGroupMenuRepository.selectList(
                AuthorityRelPermissionGroupMenuQuery.builder().menuCodeList(buttonCodeList).build()
        );
        if (CollectionUtil.isEmpty(permissionGroupMenuList)) {
            throw new ServiceException("权限异常:无授权");
        }
        List<String> groupCodeList = permissionGroupMenuList.stream()
                .map(AuthorityRelPermissionGroupMenuVO::getGroupCode).distinct().collect(Collectors.toList());
        int count2 = permissionGroupUserRepository.count(
                AuthorityRelPermissionGroupUserQuery.builder().user(user).groupCodeList(groupCodeList).build()
        );
        if (count2 <= 0) {
            throw new ServiceException("权限异常:您无权访问");
        }
        return Objects.equals(apiTabulation.getSynchronizationType(), AuthorityBaseApiTabulationSynchronizationTypeEnum.SYNCHRONIZATION.getCode());
    }

    @Override
    public AuthorityBaseApiTabulation add(AuthorityBaseApiTabulationCommand command) throws ServiceException {
        AuthorityBaseApiTabulation model = this.convertToModel(command.getUrl(), command.getMethod(), command.getPermissionType(), command.getSynchronizationType());
        repository.insert(model);
        return model;
    }

    /**
     * 通过模板批量新增API列表定义
     *
     * @param command
     * @throws ServiceException
     */
    public void addBatchByTemplate(AuthorityBaseApiTabulationTemplateCommand command) throws ServiceException {
        List<AuthorityBaseApiTabulation> modelList = new ArrayList<>();
        String url = CodeGenerateUtil.convertToUrl(command.getTableName());
        Map<Integer, String> methodMap = new HashMap<>();
        for (AuthorityBaseApiTabulationMethodEnum value : AuthorityBaseApiTabulationMethodEnum.values()) {
            methodMap.put(value.getCode(), value.getCodeSign());
        }
        for (AuthorityBaseApiTabulationTemplateEnum value : AuthorityBaseApiTabulationTemplateEnum.values()) {
            String code = CodeGenerateUtil.generateApiTabulationCode(String.format(value.getTemplate(), url), methodMap.getOrDefault(value.getMethod(), ""));
            if (!command.getBackendList().contains(code)) {
                continue;
            }
            modelList.add(
                    this.convertToModel(String.format(value.getTemplate(), url), value.getMethod(), AuthorityBaseApiTabulationPermissionTypeEnum.AUTHENTICATION.getCode(), AuthorityBaseApiTabulationSynchronizationTypeEnum.NOT_SYNCHRONIZATION.getCode())
            );
        }
        repository.insertBatch(modelList);
    }

    @Override
    public boolean deleteBatch(List<Long> ids) throws ServiceException {
        List<AuthorityBaseApiTabulationVO> modelList = repository.selectListByIds(ids);
        List<String> codeList = modelList.stream()
                .map(AuthorityBaseApiTabulationVO::getCode).distinct().collect(Collectors.toList());
        buttonApiRepository.physicalDelete(AuthorityRelButtonApiQuery.builder().apiCodeList(codeList).build());
        return repository.physicalDeleteBatchByIds(ids) > 0;
    }

    @Override
    public Boolean edit(AuthorityBaseApiTabulationCommand command) throws ServiceException {
        AuthorityBaseApiTabulationVO oldModel = repository.selectById(command.getId());
        AuthorityBaseApiTabulation model = this.convertToModel(command.getUrl(), command.getMethod(), command.getPermissionType(), command.getSynchronizationType());
        model.setId(command.getId());
        repository.updateById(model);
        buttonApiRepository.update(
                AuthorityRelButtonApi.builder().apiCode(model.getCode()).build(),
                AuthorityRelButtonApiQuery.builder().apiCode(oldModel.getCode()).build()
        );
        return true;
    }

    /**
     * 修改API列表定义激活状态
     *
     * @param command
     * @throws ServiceException
     */
    public void editActivation(AuthorityBaseApiTabulationActivationCommand command) throws ServiceException {
        repository.update(
                AuthorityBaseApiTabulation.builder().activation(command.getActivation()).build(),
                AuthorityBaseApiTabulationQuery.builder().ids(command.getIds()).build()
        );
    }

    /**
     * 修改API列表定义关联菜单
     *
     * @param command
     * @throws ServiceException
     */
    public void editMenu(AuthorityBaseApiTabulationMenuCommand command) throws ServiceException {
        AuthorityBaseApiTabulationVO model = repository.selectById(command.getId());
        List<AuthorityRelButtonApiVO> buttonApiList = buttonApiRepository.selectList(
                AuthorityRelButtonApiQuery.builder().apiCode(model.getCode()).build()
        );
        List<AuthorityRelButtonApi> needInsertApiList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(buttonApiList)) {
            List<Long> needDeleteList = buttonApiList.stream().map(AuthorityRelButtonApiVO::getId).collect(Collectors.toList());
            buttonApiRepository.physicalDeleteBatchByIds(needDeleteList);
        }
        if (CollectionUtil.isNotEmpty(command.getMenuList())) {
            for (String menu : command.getMenuList()) {
                needInsertApiList.add(AuthorityRelButtonApi.builder().apiCode(model.getCode()).buttonCode(menu).build());
            }
            buttonApiRepository.insertBatch(needInsertApiList);
        }
    }

    /**
     * 查询API列表表单信息
     *
     * @param url
     * @param method
     * @return
     * @throws ServiceException
     */
    public AuthorityBaseApiTabulationInfoVO queryInfo(String url, Integer method) throws ServiceException {
        AuthorityBaseApiTabulation model = convertToModel(url, method, null, null);
        return AuthorityBaseApiTabulationInfoVO.builder()
                .belongService(model.getBelongService())
                .code(model.getCode())
                .url(model.getUrl())
                .method(model.getMethod())
                .build();
    }

    /**
     * 查询API列表模板
     *
     * @param tableName
     * @return
     * @throws ServiceException
     */
    public List<AuthorityBaseApiTabulationInfoVO> queryTemplate(String tableName) throws ServiceException {
        String url = CodeGenerateUtil.convertToUrl(tableName);
        List<String> codeList = CodeGenerateUtil.generateApiTabulationCode(url);
        List<AuthorityBaseApiTabulationVO> apiList = repository.selectList(
                AuthorityBaseApiTabulationQuery.builder().codeList(codeList).build()
        );
        List<String> apiCodeList = apiList.stream()
                .map(AuthorityBaseApiTabulationVO::getCode).distinct().collect(Collectors.toList());
        List<AuthorityBaseApiTabulationInfoVO> resultList = new ArrayList<>();
        for (AuthorityBaseApiTabulationTemplateEnum value : AuthorityBaseApiTabulationTemplateEnum.values()) {
            AuthorityBaseApiTabulationInfoVO model = this.queryInfo(String.format(value.getTemplate(), url), value.getMethod());
            if (apiCodeList.contains(model.getCode())) {
                continue;
            }
            resultList.add(model);
        }
        return resultList;
    }

    /**
     * 根据ID查询API列表关联菜单
     *
     * @param id
     * @return
     * @throws ServiceException
     */
    public AuthorityBaseApiTabulationMenuVO queryMenuById(Long id) throws ServiceException {
        AuthorityBaseApiTabulationVO model = repository.selectById(id);
        List<AuthorityBaseMenuTabulationVO> menuList = Collections.emptyList();
        List<AuthorityRelButtonApiVO> buttonApiList = buttonApiRepository.selectList(
                AuthorityRelButtonApiQuery.builder().apiCode(model.getCode()).build()
        );
        if (CollectionUtil.isNotEmpty(buttonApiList)) {
            List<String> codeList = buttonApiList.stream()
                    .map(AuthorityRelButtonApiVO::getButtonCode).distinct().collect(Collectors.toList());
            menuList = menuTabulationRepository.selectList(
                    AuthorityBaseMenuTabulationQuery.builder().codeList(codeList).build()
            );
        }
        return AuthorityBaseApiTabulationMenuVO.builder()
                .code(model.getCode())
                .menuList(menuList)
                .build();
    }

    @Override
    public Page<AuthorityBaseApiTabulationVO> query(AuthorityBaseApiTabulationQuery query) throws ServiceException {
        Page<AuthorityBaseApiTabulationVO> resultPage = super.query(query);
        if (resultPage != null && CollectionUtil.isNotEmpty(resultPage.getRecords())) {
            List<String> codeList = resultPage.getRecords().stream()
                    .map(AuthorityBaseApiTabulationVO::getCode).distinct().collect(Collectors.toList());
            List<KeyCountModel> keyCountList = buttonApiRepository.countButtonCodeGroupByApiCode(codeList);
            Map<String, Integer> codeCountMap = keyCountList.stream()
                    .collect(Collectors.toMap(KeyCountModel::getKey, KeyCountModel::getNum, Integer::sum));
            Map<Integer, String> methodMap = EnumUtil.getCodeToNameMap(AuthorityBaseApiTabulationMethodEnum.values());
            Map<Integer, String> activationMap = EnumUtil.getCodeToNameMap(ActivationEnum.values());
            for (AuthorityBaseApiTabulationVO tabulationVO : resultPage.getRecords()) {
                tabulationVO.setMethodName(methodMap.getOrDefault(tabulationVO.getMethod(), ""));
                tabulationVO.setFrontEndCount(codeCountMap.getOrDefault(tabulationVO.getCode(), 0));
                tabulationVO.setActivationName(activationMap.getOrDefault(tabulationVO.getActivation(), ""));
            }
        }
        return resultPage;
    }

    /**
     * 生成对象
     *
     * @param url
     * @param method
     * @param permissionType
     * @param synchronizationType
     * @return
     * @throws ServiceException
     */
    private AuthorityBaseApiTabulation convertToModel(String url, Integer method, Integer permissionType, Integer synchronizationType) throws ServiceException {
        AuthorityBaseApiTabulationMethodEnum methodEnum = EnumUtil.getEnumByCode(method, AuthorityBaseApiTabulationMethodEnum.values());
        if (methodEnum == null) {
            throw new ServiceException("执行异常: 请求方法不存在");
        }
        String code = CodeGenerateUtil.generateApiTabulationCode(url, methodEnum.getCodeSign());
        return AuthorityBaseApiTabulation.builder()
                .belongService(code.substring(0, code.indexOf("-")))
                .code(code)
                .url(url)
                .method(method)
                .permissionType(permissionType)
                .activation(ActivationEnum.NOT_ACTIVE.getCode())
                .synchronizationType(synchronizationType)
                .build();
    }

}
